package bogus.core;

import com.jogamp.opengl.DebugGL3;
import com.jogamp.opengl.GL3;
import com.jogamp.opengl.GLAutoDrawable;
import com.jogamp.opengl.GLEventListener;

import audio.Audio;
import bogus.settings.Settings;
import bogus.userInput.Input;
import owg.util.opengl.EGL3;
import owg.util.Calc;

public class BogusRenderer implements GLEventListener
{
    private static final boolean DEBUG = true;

    /**Used to slow down tick rate*/
    public float time = 1;
    /**Keeps track of the time...*/
    public long currentStep;
    /**Inter-step interpolation factor. <br>
     * If frame rate is higher than step rate, then this number indicates when the current frame is being rendered in relation to the two neighboring steps.<br>
     * For example, in the ideal case, this number will oscillate between 0 and 0.5 if frame rate is 60 and step rate is 30.*/
    public float interp;

    /**The time between each SPS measurement, in milliseconds.*/
    private int spsCountPeriod = 1000;
    /**The number of step events counted in the second before {@link #lastStepCountTime}. Updated approximately every {@link #spsCountPeriod} milliseconds.*/
    private float lastSPSCount;
    /**The time of the last SPS count update, in milliseconds.*/
    private double lastStepCountTime;
    /**Used to measure the number of steps in the last second*/
    private int stepCounter;
    /**The time of the last step, in milliseconds.*/
    Double vbrGameClock;

    void restartClock()
    {
	vbrGameClock = null;
    }

    public float getLastSPS()
    {
	return lastSPSCount;
    }

    @Override
    public void init(GLAutoDrawable drawable)
    {
	final GL3 gl;
	if(DEBUG)
	{
	    gl = new DebugGL3(drawable.getGL().getGL3());
	    drawable.setGL(gl);
	}
	else
	    gl = drawable.getGL().getGL3();
	
	System.out.println("Renderer init");

	BogusCore.egl = new EGL3(gl, Settings.settings.forcePOTS, Settings.settings.verticalSync);
    }

    @Override
    public void dispose(GLAutoDrawable drawable)
    {
	System.out.println("Renderer dispose");
	BogusCore.getMinigame().dispose();
	BogusCore.egl.dispose();
    }

    @Override
    public void display(GLAutoDrawable drawable)
    {
	//The time between game ticks in milliseconds
	double tickMs = (1000.0/(BogusCore.getTargetSPS()*time));
	try 
	{
	    if(vbrGameClock == null) 
	    {
		//Guarantee that at least one step event is dispatched before the first render
		step();
		//Sensible defaults
		vbrGameClock = (double)System.currentTimeMillis();
		lastSPSCount = BogusCore.getTargetSPS();
		lastStepCountTime = vbrGameClock;
		stepCounter = 0;

	    }

	    //Prevent accumulation of too much lag, in a way that is consistent with constant rate behaviour
	    //long minTime = System.currentTimeMillis()-(1000*bogusCore.getTargetFPS())/bogusCore.getTargetSPS();
	    //if(vbrGameClock < minTime)
	    //vbrGameClock = minTime;
	    
	    //Measure time since last step
	    double dt = System.currentTimeMillis() - vbrGameClock;
	    //Execute as many steps as necessary to catch up
	    while(dt >= tickMs) 
	    {
		dt -= tickMs;
		vbrGameClock += tickMs;
		step();
		if(vbrGameClock == null)
		    return;
		
		stepCounter++;
		if(vbrGameClock > lastStepCountTime+spsCountPeriod)
		{
		    
		    lastSPSCount = ((1000f*stepCounter)/(float)(vbrGameClock-lastStepCountTime));
		    stepCounter = 0;
		    lastStepCountTime = vbrGameClock;
		}
	    }
	    //dt is now between 0 and tickMs
	    //this means the clock is between current time and current time - tickMs
	}
	catch(Exception e)
	{
	    System.err.println("Error in step: ");
	    e.printStackTrace();
	}
	
	if(BogusCore.getTargetFPS() > BogusCore.getTargetSPS())
	    interp = Calc.limit(-(float)((vbrGameClock - System.currentTimeMillis())/tickMs), 0, 1);
	else
	    interp = 1;

	try 
	{
	    render();
	}
	catch(Exception e)
	{
	    System.err.println("Error in render: ");
	    e.printStackTrace();
	}
    }
    private void step() 
    {
	BogusCore.getMinigame().step();
	BogusCore.egl.step();
	Audio.step();
	Input.framePressRefresh();
    }
    private void render()
    {
	BogusCore.getMinigame().render();
	BogusCore.canvas.swapBuffers();
    }

    @Override
    public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height)
    {
	BogusCore.egl.setViewport(x, y, width, height);
    }

    public EGL3 getEGL()
    {
	return BogusCore.egl;
    }

    public float getInterp() {
	return interp;
    }
}
